import {
  BaseCommand,
  CENTEREVENTS,
  CSPoint,
  CommandDialog,
  CommandDialogPlacement,
  POINT_TYPE,
  QueryParamKeys,
  getQueryParam,
} from '@amcax/base';
import { getAirfoilVertexes } from '../cmd-airfoil/cmdAirfoilAPI';
import { Box, Button, FormControl, FormGroup, TextField } from '@mui/material';
import { SelectLayout } from './styles';
import { useEffect, useState } from 'react';
import { Scene, Vector3 } from 'three';
import { csEvent } from '../../../../common/events/EventCenter';
import { Renderer3D, RendererMgr, Snaper } from '@amcax/renderer';
import { wsApi } from '../../../../server';
import { Modal, Spin, message } from 'antd';
import { clearLocalListDatas } from '../../widgets/RightNavigation/panels/InputPanel';
import { ctx2, previewer, userEvents } from '../../../../module';

interface EditVertexCommandProps {
  title: string;
  onClose: () => void;
  placement: CommandDialogPlacement;
}

interface Point {
  x: number;
  y: number;
  index: number;
}

const documentId = getQueryParam<string>(QueryParamKeys.DOCUMENT_ID);
const keyPointsScene = new Scene();
const assisGroup = ctx2.cadScene.assisGroup;
const viewPointsScene = new Scene();
const workGroup = ctx2.cadScene.workGroup;
const keyPointsMap = new Map<number, Vector3>();
let nowTakenPoint = null,
  pointObj = null,
  pickedPointRed = null,
  criticalPointIndex = null; // 点前进方向（x递增/x递减）

function EditVertexCommand({
  title,
  onClose,
  placement,
}: EditVertexCommandProps) {
  const [keyPoint, setKeyPoint] = useState<Point>({ x: 0, y: 0, index: null });
  const [canExecute, setCanExecute] = useState<boolean>(false);
  const [obj, setObj] = useState<any>(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    startCmd();
  }, []);

  useEffect(() => {
    if (keyPoint.index !== null) {
      if (pickedPointRed) {
        pickedPointRed.setVertex(new Vector3(keyPoint.x, keyPoint.y, 0));
      }
      showPriview();
      setCanExecute(true);
    }
  }, [keyPoint]);

  const initParams = () => {
    setKeyPoint({ x: 0, y: 0, index: null });
    setObj(null);
    setCanExecute(false);
  };

  const startCmd = () => {
    registerEvents();
    drawKeyPoints();
    startAction();
  };

  const exitCmd = () => {
    unRegisterEvents();
    clearKeyPoints();
    abortAction();
    previewer.clear();
    initParams();
    onClose();
  };

  // ================= 事件 ========================

  const registerEvents = () => {
    csEvent()
      .setState('editVertex')
      .on(CENTEREVENTS.POINTERMOVE, onPointerMove)
      .on(CENTEREVENTS.POINTERUP, onPointerUp);
  };

  const unRegisterEvents = () => {
    csEvent()
      .off(CENTEREVENTS.POINTERMOVE, onPointerMove)
      .off(CENTEREVENTS.POINTERUP, onPointerUp)
      .setState('airfoil');
    // .setDefault();
  };

  const onPointerMove = (e) => {
    const picked = Snaper.snapKeyPoint(e, {
      lineCross: false,
      faceCross: false,
    });
    if (picked) {
      nowTakenPoint = picked.vector;
      pointObj = picked.pointObj;
    } else {
      pointObj = null;
      nowTakenPoint = null;
    }
    ctx2.viewer.requestRender();
  };

  const onPointerUp = (e) => {
    if (!e.button.onLeft) return;

    if (nowTakenPoint && pointObj) {
      setObj(pointObj);
      setKeyPoint({
        x: nowTakenPoint.x,
        y: nowTakenPoint.y,
        index: pointObj.userData.index,
      });
      if (pickedPointRed) {
        pickedPointRed.setVertex(
          new Vector3(nowTakenPoint.x, nowTakenPoint.y, 0),
        );
      } else {
        pickedPointRed = new CSPoint({
          vertex: new Vector3(nowTakenPoint.x, nowTakenPoint.y, 0),
          pointType: POINT_TYPE.DEFAULT,
          size: 12,
          color: 0x00ff00,
        });
        workGroup.add(pickedPointRed);
      }
    } else {
      if (pickedPointRed) {
        workGroup.remove(pickedPointRed);
        pickedPointRed.destroy();
        pickedPointRed = null;
      }
      previewer.clear();
      initParams();
    }
    ctx2.viewer.requestRender();
  };

  // ================= 关键点 ========================

  // 绘制原始观察点
  const drawViewPoints = async (vertexes: Vector3[]) => {
    for (let i = 0, l = vertexes.length; i < l; i++) {
      const assistPointt = new CSPoint({
        vertex: vertexes[i].clone(),
        pointType: POINT_TYPE.DEFAULT,
        size: 6,
      });
      viewPointsScene.add(assistPointt);
    }
    workGroup.add(viewPointsScene);
    ctx2.viewer.requestRender();
  };

  // 绘制可拾取关键点
  const drawKeyPoints = async () => {
    const vertexes = await getAirfoilVertexes();
    drawViewPoints(vertexes);
    for (let i = 0, l = vertexes.length; i < l; i++) {
      const assistPointt = new CSPoint({
        vertex: vertexes[i].clone(),
        pointType: 'transparent',
        size: 10,
        index: i + 1,
        // color: 0xff0000,
      });
      if (vertexes[i].x === 0 && vertexes[i].y === 0) {
        criticalPointIndex = assistPointt.userData.index;
      }
      keyPointsMap.set(assistPointt.userData.index, vertexes[i]);
      keyPointsScene.add(assistPointt);
    }
    assisGroup.add(keyPointsScene);
    ctx2.viewer.requestRender();
  };

  const updateKeyPoints = () => {
    keyPointsScene.clear();
    viewPointsScene.clear();
    const vertexes = Array.from(keyPointsMap.values());
    keyPointsMap.clear();
    for (let i = 0, l = vertexes.length; i < l; i++) {
      const viewPoints = new CSPoint({
        vertex: vertexes[i].clone(),
        pointType: POINT_TYPE.DEFAULT,
        size: 6,
      });
      viewPointsScene.add(viewPoints);
      const keyPoints = new CSPoint({
        vertex: vertexes[i].clone(),
        pointType: 'transparent',
        size: 10,
        index: i + 1,
        // color: 0xff0000,
      });
      keyPointsScene.add(keyPoints);
      keyPointsMap.set(keyPoints.userData.index, vertexes[i]);
    }
  };

  const clearKeyPoints = () => {
    if (pickedPointRed) {
      workGroup.remove(pickedPointRed);
      pickedPointRed.destroy();
      pickedPointRed = null;
    }
    keyPointsScene.clear();
    assisGroup.remove(keyPointsScene);
    viewPointsScene.clear();
    workGroup.remove(viewPointsScene);
    keyPointsMap.clear();
    ctx2.viewer.requestRender();
  };

  const checkLegality = () => {
    let legal = true;
    const length = keyPointsMap.size;
    const curPoint = keyPointsMap.get(keyPoint.index);
    const prePoint = keyPointsMap.get(keyPoint.index - 1);
    const nextPoint = keyPointsMap.get(keyPoint.index + 1);
    if (keyPoint.index < criticalPointIndex && keyPoint.index > 1) {
      // x递减
      if (curPoint.x > prePoint.x || curPoint.x < nextPoint.x) {
        legal = false;
      }
    } else if (keyPoint.index > criticalPointIndex && keyPoint.index < length) {
      // x递增
      if (curPoint.x < prePoint.x || curPoint.x > nextPoint.x) {
        legal = false;
      }
    } else if (keyPoint.index === criticalPointIndex) {
      // 零点
      legal = false;
    } else if (keyPoint.index === 1) {
      // 起点
      if (curPoint.x < nextPoint.x) {
        legal = false;
      }
    } else if (keyPoint.index === length) {
      // 终点
      if (curPoint.x < prePoint.x) {
        legal = false;
      }
    }

    return legal;
  };

  // ================= 执行 ========================

  const handleExecute = async () => {
    if (!checkLegality()) {
      message.error('此位置不合法，请继续调整');
      return;
    }
    setLoading(true);
    previewer.clear();
    await abortAction();
    await updateAirfoil();
    await updateKeyPoints();
    ctx2.viewer.requestRender();
    if (pickedPointRed) {
      workGroup.remove(pickedPointRed);
      pickedPointRed.clear();
      pickedPointRed = null;
    }

    // await startAction();
    // initParams();
    setLoading(false);
    exitCmd();
  };

  const updateAirfoil = async () => {
    const dxidJSON = await wsApi.getAppData(documentId + 'dxid');
    const dxid = JSON.parse(dxidJSON);
    const valuesArray = Array.from(keyPointsMap.values());
    const _points = valuesArray.map((point: Vector3) => {
      return [point.x, point.y, point.z];
    });
    await wsApi.setParams(dxid, { points: _points });
    clearLocalListDatas();
  };

  // ================= 预览 ========================

  const showPriview = () => {
    const point = new Vector3(keyPoint.x, keyPoint.y, 0);
    keyPointsMap.set(keyPoint.index, point);
    const valuesArray = Array.from(keyPointsMap.values());
    previewAirfoil(valuesArray);
  };

  const previewAirfoil = async (data) => {
    const _points = data.map((point: Vector3) => {
      return [point.x, point.y, point.z];
    });
    await wsApi
      .getEngine()
      .execCmd({
        c: 'ExecuteTransaction',
        type: 'Bspline',
        a: { mode: 'Approx', points: _points },
      })
      .then((res) => {
        const models = getPreviewData(res);
        if (models?.length > 0) {
          previewer.clear();
          previewer.preview(models);
        }
      });
    ctx2.viewer.requestRender();
  };

  const startAction = async () => {
    await wsApi.getEngine().execCmd({
      c: 'StartTransaction',
      n: 'CreateCurve',
    });
  };

  const abortAction = async () => {
    await wsApi.getEngine().execCmd({
      c: 'AbortTransaction',
    });
  };

  const endAction = async () => {
    await wsApi.getEngine().execCmd({
      c: 'EndTransaction',
    });
  };

  const getPreviewData = (data) => {
    return RendererMgr.getRender<Renderer3D>('3d').parsePreviewModels(data);
  };

  // ================= 组件 ========================

  /**修改表单参数 */
  const handleDataChange = (minValue, maxValue) => (event) => {
    let { name, value, type } = event.target;
    if (type === 'number') value = Number(value);
    if (isNaN(value)) {
      return;
    }
    if (value < minValue) {
      value = minValue;
    } else if (value > maxValue) {
      value = maxValue;
    }
    setKeyPoint((prevState) => ({
      ...prevState,
      [name]: value,
    }));
  };

  const Select = ({ title }) => {
    return (
      <div className='select active'>
        <div className='label'>{title}</div>
        {obj ? (
          <>
            <div
              className='body'
              key={obj.name}>
              {obj.type + ':' + obj.userData.index}
            </div>
          </>
        ) : (
          <div className='placeholder'>{`[请选择${title}]`}</div>
        )}
      </div>
    );
  };

  return (
    <CommandDialog
      title={title}
      onClose={exitCmd}
      placement='topLeft'>
      <Box
        sx={{
          padding: '0 10px',
        }}>
        <FormGroup>
          <SelectLayout>
            <Select title='关键点' />
          </SelectLayout>
          <FormControl variant='filled'>
            <TextField
              name='x'
              label='X'
              type='number'
              inputProps={{ min: 0.0, max: 1.0 }}
              size='small'
              margin='normal'
              value={keyPoint.x}
              onChange={handleDataChange(0.0, 1.0)}
            />
          </FormControl>
          <FormControl variant='filled'>
            <TextField
              name='y'
              label='Y'
              type='number'
              inputProps={{ min: -1.0, max: 1.0 }}
              size='small'
              margin='normal'
              value={keyPoint.y}
              onChange={handleDataChange(-1.0, 1.0)}
            />
          </FormControl>
          <Box
            sx={{
              padding: '5px 0',
              display: 'flex',
              justifyContent: 'flex-end',
            }}>
            <Button
              size='small'
              variant='contained'
              disabled={!canExecute}
              onClick={handleExecute}>
              执行
            </Button>
          </Box>
        </FormGroup>
      </Box>
      <Modal
        open={loading}
        footer={null}
        closable={false}
        centered
        className='transparent-modal'>
        <div style={{ textAlign: 'center', padding: '20px' }}>
          <Spin
            tip='Loading...'
            spinning={loading}
            className='custom-spin'
          />
        </div>
      </Modal>
    </CommandDialog>
  );
}

export const cmdEditVertex: BaseCommand = {
  id: 'cmdEditVertex',
  icon: 'commands/airfoil/edit-point-coordinate',
  label: '调整翼型点位置',
  create: (label: string, onClose: () => void) => (
    <EditVertexCommand
      title={'调整翼型点位置'}
      onClose={onClose}
      placement='topLeft'
    />
  ),
  initialDisabled: true,
  subscribeDisabled: (setDisabled: (disabled: boolean) => void) => {
    const listener = (empty: boolean) => {
      setDisabled(empty);
    };
    userEvents.on('sceneEmpty', listener);
    return () => userEvents.off('sceneEmpty', listener);
  },
};
